// lex.hpp
// A generic builder for lexers.
//
// Copyright (C) 2008  Maksim Sipos <msipos@mailc.net>
//
// This file is a part of the Ripe language project.
//
// Ripe is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#ifndef R_LEX_HPP
#define R_LEX_HPP

#include "util/standard.hpp"
#include <istream>
#include "util/nfa.hpp"
#include "adts/vector.hpp"

namespace Ripe {
  // General Lexer error.
  class ErrorLexer : public Exception {};

  // Thrown when the Lexer can't match the given text to any rule.
  class ErrorLexerCantMatch : public ErrorLexer {};

  // Thrown when the matched token's size is bigger than the token buffer.
  class ErrorBigToken : public ErrorLexer {};

  struct LexerRule {
    // id of the token returned
    int id;

    // After matching the rule, to_env becomes the environment.
    int to_env;
  };

  // Reserved values of id:
  #define ID_EOF -1
  #define ID_CONSUME -2 // (Just consume the token and don't return)

  // Possible values of to_env:
  #define ENV_POP -1    // Pop previous environment off the stack
  #define ENV_STAY -2   // Stay in the current environment

  class Lexer {
    public:
      Lexer(uint32_t environments);

      // Read a token from the input.
      int read_token();

      // Buffer used for a recorded token
      #define R_LEXER_TOKSIZE 511
      char token[R_LEXER_TOKSIZE + 1];

      // Add a rule to the lexer. Rule will belong to the given environment.
      // After matching this rule, to_env will become the new environment.
      // id will be returned when a match occurs. If to_env is ENV_POP, then
      // an environment will be popped off the stack. If to_env ENV_STAY,
      // there will be no change.
      void add_rule(int env, int to_env, const char* pattern, int id);

      // Initialize the lexer to read from the given input stream.
      void set_input(std::istream* i);
    private:
      std::istream* input;

      // List of rules that are applied when matching the input.
      Vector<LexerRule> rules;

      // Stack of environments.
      Vector<int> stack;

      // Number of environments.
      uint32_t envs;

      // NFAs (one for each environment).
      NFASplitNode** nfas;

      // Buffer used for the lexer
      #define R_LEXER_BUFSIZE 4095
      char buf[R_LEXER_BUFSIZE + 1];

      // Amount of data available in the buffer
      uint32_t buf_len;

      // Current index in the buffer
      uint32_t idx;

      // Read a token from the input.
      int read_raw_token();

      // Rewind the buffer
      void rewind_buf(void);

      // Matched an id
      int matched(int matched_id, int matched_len) throw (ErrorBigToken);
  };
}

#endif /* R_LEX_HPP */
